package com.mamingchao.concurrent.thread_communication2;

import org.apache.commons.lang3.StringUtils;

import java.util.EmptyStackException;
import java.util.Stack;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 *
 *  信号量实现了，同步容器怎么实现？
 *
 *  两个信号量 分别限制生产者和消费者的并发量，限流
 *  读写锁，实现线程安全，同步锁
 *
 *  ReadWriteLock ,读的时候 10个并发读没问题；写的时候，两个线程同时写，writeLock 只能同一个时间只有一个线程写。
 *  所以不能用ReadWriteLock，用ReentrantLock 写一个读操作和写操作互斥的锁，写操作本身线程直接不需要锁
 *  ReentrantLock 也不行，还不如ReadWriteLock，他不管是什么线程，是读线程还是写线程，都只能抢到锁才能复制
 *
 *
 *  下面的这个实现，没实现的是阻塞。即当容器满的时候，生产者应该阻塞住，而不是直接返回【The container is full now】
 *  当生产者 空的时候，消费者也应该阻塞住，而不是直接返回 【null】
 *
 * Created by mamingchao on 2020/9/20.
 */
public class SyncContainer {

    /*
        容器固定容量 为10
     */
    final int capacity = 8;

//    List container = Collections.synchronizedList(new ArrayList<Object>(capacity));

    Stack stackContainer = new Stack();

//    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//    Lock readLock = readWriteLock.readLock();
//    Lock writeLock = readWriteLock.writeLock();

    static volatile String signal = "";
    static final String WRITING = "write";
    static final String READING = "read";


    //存放的信号量
    Semaphore putSemaphore = new Semaphore(2);
    //读取的信号量
    Semaphore getSemaphore = new Semaphore(10);

    /**
     * 如果容器已满，直接返回false
     * 容器的写方法
     * @param  o
     * @return true false
     */
    public boolean put(Object o) {

        //检查signal 状态，轮训check
        while (true) {
            //如果有线程正在读，那么写线程循环等待,每次等待0.1秒
            if (StringUtils.isNotBlank(signal) && READING.equalsIgnoreCase(signal)) {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {//如果signal 是空，是signal 的初始状态，说明都还没有读过或者写过，推出while循环 可以往下读
                //或者已经是写状态，也可以继续往下走；具体能不能执行，看是否容器已满，或者能不能抢到信号量
                signal = WRITING;
                break;
            }
        }

//        writeLock.lock();
        if (stackContainer.size() == capacity) {
            System.out.println("The container is full");
//            writeLock.unlock();
            return false;
        }


        try {
            putSemaphore.acquire();
            stackContainer.push(o);
            TimeUnit.SECONDS.sleep(1);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        putSemaphore.release();
//        writeLock.unlock();
        return true;
    }


    /**
     * 如果容器已空，直接返回null
     * 容器的读方法
     * @return true false
     */
    public Object get() {
        //检查signal 状态，轮训check
        while (true) {
            //如果有线程正在读，那么写线程循环等待,每次等待0.1秒
            if (StringUtils.isNotBlank(signal) && WRITING.equalsIgnoreCase(signal)) {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {//如果signal 是空，是signal 的初始状态，说明都还没有读过或者写过，推出while循环 可以往下读
                //或者已经是写状态，也可以继续往下走；具体能不能执行，看是否容器已满，或者能不能抢到信号量
                signal = READING;
                break;
            }
        }

//        readLock.lock();
        try {
            getSemaphore.acquire();
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (stackContainer.empty()) {
            getSemaphore.release();
//            readLock.unlock();
            return null;
        } else {
            try {
                Object result = stackContainer.pop();

                getSemaphore.release();
                return result;
            } catch (EmptyStackException e) {
                getSemaphore.release();
//            readLock.unlock();
                return null;
            }

//        readLock.unlock();
        }
    }




    public static void main(String[] args) {
        SyncContainer sc = new SyncContainer();

        for (int i = 0; i < 10; i++) {
            String threadName = "put thread--" + i;
            String value = i + "";
            new Thread(()->{
                boolean result = sc.put(value);
                System.out.println(threadName + "  has put in " + value + " and the result is " + result);
            }).start();
        }

        for (int i = 0; i < 20; i++) {
            String threadName = "get thread--" + i;
            new Thread(()->{
                Object v = sc.get();
                System.out.println(threadName + " has get " + v);
            }).start();
        }
    }
}
